package org.simple.framework.html.parser.handler;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.simple.framework.html.parser.annotation.HtmlSelect;
import org.simple.framework.html.parser.annotation.Resource;
import org.simple.framework.html.parser.convert.SimpleTypeConverter;
import org.simple.framework.html.parser.convert.TypeConverter;
import org.simple.framework.html.parser.enums.SelectType;
import org.simple.framework.html.parser.process.ComplexModel;
import org.simple.framework.html.parser.process.PreProcesser;
import org.simple.framework.html.parser.reflect.ReflectUtil;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.stream.Collectors;

/**
 * 项目名称：simple-framework
 * 类名称：Jsoup
 * 类描述：Jsoup
 * 创建时间：2018/9/21
 *
 * @author sisyphus   (E-mail:1620657419@qq.com)
 * @version v1.0
 */
public class JsoupHandler {

    private static TypeConverter typeConverter;

    static {
        JsoupHandler.typeConverter = new SimpleTypeConverter();
    }


    public static <T> T handle(Class<T> tClass, Element element) {
        T t = ReflectUtil.getInstance(tClass);

        //注册预处理器
        PreProcesser preProcesser = new PreProcesser();
        if (t instanceof ComplexModel) {
            ((ComplexModel) t).register(preProcesser);
        }

        Field[] declaredFields = tClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            HtmlSelect annotation = declaredField.getAnnotation(HtmlSelect.class);
            if (annotation == null) {
                continue;
            }

            Elements elements;
            if (annotation.value().isEmpty()) {
                elements = new Elements(element);
            } else {
                elements = element.select(annotation.value());
            }

            Class<?> fieldType = declaredField.getType();

            if (fieldType.isArray()) {
                Object[] collect = elements.stream().map(
                        e -> getFieldValue(e, fieldType, annotation, preProcesser)
                ).toArray(String[]::new);
                ReflectUtil.setFieldValue(t, declaredField, collect);
            } else if (Collection.class.isAssignableFrom(fieldType)) {
                Class aClass = (Class) (((ParameterizedType) declaredField.getGenericType()).getActualTypeArguments())[0];
                Object collect = elements.stream().map(
                        e -> getFieldValue(e, aClass, annotation, preProcesser)
                ).collect(Collectors.toList());
                ReflectUtil.setFieldValue(t, declaredField, collect);
            } else {
                if (elements.size() > annotation.index()) {
                    Object value = getFieldValue(elements.get(annotation.index()), declaredField.getType(), annotation, preProcesser);
                    ReflectUtil.setFieldValue(t, declaredField, value);
                }
            }
        }

        //调用构造完成回调
        if (t instanceof ComplexModel) {
            ((ComplexModel) t).finish();
        }
        return t;
    }

    public static <T> T handle(Class<T> tClass) throws IOException {
        if (!tClass.isAnnotationPresent(Resource.class)) {
            throw new UnsupportedOperationException("请使用@Resource配置默认资源");
        }
        Resource resource = tClass.getAnnotation(Resource.class);
        Document element = Jsoup.connect(resource.value()).get();
        return handle(tClass, element);
    }

    private static <T> T getFieldValue(Element element, Class<T> t, HtmlSelect htmlSelect, PreProcesser preProcesser) {
        if (typeConverter.isSupported(t)) {
            String rawValue = getValue(element, htmlSelect);
            Object value = preProcesser.process(htmlSelect.preProcess(), rawValue);
            return (T) typeConverter.convert(value, t);
        } else {
            return (T) handle(t, element);
        }
    }

    private static String getValue(Element element, HtmlSelect htmlSelect) {
        if (element == null) {
            return null;
        }

        SelectType type = htmlSelect.type();
        switch (type) {
            case HTML:
                return element.html();
            case TEXT:
                return element.text();
            case ATTR:
                return element.attr(htmlSelect.attr());
            default:
                return null;
        }
    }

}
